#include <string>
#include <iostream>
#include <mysql/mysql.h>

enum ErrNo
{
    mysqlInitErr = 1,
    mysqlConnErr
};

class DataBase
{
public:
    static DataBase* GetInstance()
    {
        if (_instance == nullptr) {
            _instance = new DataBase();
        }
        return _instance;
    }

public:
    void Connect(const std::string& host, const std::string& user, \
        const std::string& passwd, const std::string& db, const uint16_t port)
    {
        _host = host; _user = user;
        _passwd = passwd; _db = db;
        _port = port;
        MYSQL* ret = mysql_real_connect(_mysql, _host.c_str(), _user.c_str(), \
            _passwd.c_str(), _db.c_str(), _port, nullptr, 0);
        if(ret == nullptr) {
            std::cerr << "mysql Conn Err..." << std::endl; // for debug
            return;
        }
        std::cout << "mysql Conn Done..." << std::endl; // for debug
        _isConn = true;
    }

    int SetCharset(const std::string& cset)
    {
        int ret = mysql_set_character_set(_mysql, cset.c_str());
        if (ret != 0) {
            std::cerr << "set character set err..." << std::endl;
        }
        return ret;
    }

    int Query(const std::string& que)
    {
        int ret = mysql_query(_mysql, que.c_str());
        if(ret == 0) {
            std::cout << "query: -> " << que << " <-\n" << "done..." << std::endl;
        }
        else {
            std::cerr << "query: -> " << que << " <-\n" << "err..." << std::endl;
        }
        return ret;
    }

    MYSQL_RES* Select(const std::string& seQuery)
    {
        if(Query(seQuery) == 0) {
            std::cout << "select done..." << std::endl; // for debug
        }
        else {
            std::cerr << "select err..." << std::endl; // for debug
            return nullptr;
        }
        _res = mysql_store_result(_mysql);
        if (_res == nullptr) {
            std::cerr << "res store err..." << std::endl; // for debug
        }
        return _res;
    }

    uint64_t NumRows()
    {
        if (_res == nullptr) { return 0; }
        else { return mysql_num_rows(_res); }
    }

    unsigned int NumFields()
    {
        if (_res == nullptr) { return 0; }
        else { return mysql_num_fields(_res); }
    }

    MYSQL_ROW FetchRow()
    {
        if (_res == nullptr) { return nullptr; }
        else {
            return mysql_fetch_row(_res);
        }
    }

    MYSQL_FIELD* FetchFields()
    {
        if (_res == nullptr) { return nullptr; }
        else {
            return mysql_fetch_fields(_res);
        }
    }

    void ResFree()
    {
        return mysql_free_result(_res);
    }

    bool IsConn()
    {
        return _isConn;
    }

    void Close()
    {
        if (_isConn) {
            mysql_close(_mysql);
            _isConn = false;
        }
    }

    ~DataBase()
    {
        Close();
    }

private:
    DataBase()
        :_port(-1), _mysql(nullptr), _res(nullptr), _isConn(false)
    {
        Init();
    }

    DataBase(const DataBase&) = delete;
    DataBase& operator=(const DataBase&) = delete;

private:
    static DataBase* _instance;

private:
    int Init()
    {
        if((_mysql = mysql_init(_mysql)) == nullptr) {
            std::cerr << "mysql init err..." << std::endl; // for debug
            return mysqlInitErr;
        }
        std::cout << "mysql init done..." << std::endl; // for debug
        return 0;
    }

private:
    std::string _host;
    std::string _user;
    std::string _passwd;
    std::string _db;
    uint16_t _port;
    MYSQL* _mysql;
    MYSQL_RES* _res;
    bool _isConn;
};

DataBase* DataBase::_instance = nullptr;